package com.manage.competition.shiro;


import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.*;

/**
 * Create with IDEA
 *
 * @Author:Vantcy
 * @Date: Create in 15:54 2019/1/25
 * @Description: shiroConfig
 */
@Configuration
@Slf4j
public class ShiroConfig {

    @Bean(name = "retryLimitHashedCredentialsMatcher")
    public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher() {
        RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher();
        //指定加密方式为MD5
        retryLimitHashedCredentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
        //加密次数
        retryLimitHashedCredentialsMatcher.setHashIterations(1024);
        retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        log.info("=================retryLimitHashedMatch注入成功=================");
        return retryLimitHashedCredentialsMatcher;
    }

    /**
     * Sha256Hash 身份认证 realm;
     * <p>
     * 必须写这个类，并加上 @Bean 注解，目的是注入 Realm，
     * 否则会影响 Realm类 中其他类的依赖注入
     */
    @Bean(name = "authRealm")
    public AuthRealm authRealm(@Qualifier("retryLimitHashedCredentialsMatcher") RetryLimitHashedCredentialsMatcher matcher) {
        AuthRealm authRealm = new AuthRealm();
        authRealm.setAuthenticationCachingEnabled(true);
        authRealm.setCredentialsMatcher(matcher);
        log.info("=================AuthRealm类注入成功=================");
        return authRealm;
    }


    @Bean(name = "sessionListener")
    public ShiroSessionListener sessionListener() {
        ShiroSessionListener sessionListener = new ShiroSessionListener();
        log.info("=================SessionListener类注入成功=================");
        return sessionListener;
    }


    @Bean(name = "sessionManager")
    public DefaultWebSessionManager sessionManager(
            @Qualifier("sessionListener") ShiroSessionListener sessionListener) {
        CustomSessionManager sessionManager = new CustomSessionManager();

        List<SessionListener> listeners = new ArrayList<>();
        listeners.add(sessionListener);

        sessionManager.setGlobalSessionTimeout(1800000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionListeners(listeners);
        log.info("=================SessionManager类注入成功=================");
        return sessionManager;
    }
//
    /**
     * 注入 securityManager
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(
            @Qualifier("authRealm") AuthRealm authRealm,
//            @Qualifier("cacheManager") RedisCacheManager cacheManager,
            @Qualifier("sessionManager") DefaultWebSessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        Collection<Realm> realms = new HashSet<>();
        realms.add(authRealm);
        // 设置realm.
        securityManager.setRealms(realms);
        // 自定义缓存实现 使用redis
//        securityManager.setCacheManager(cacheManager);
        securityManager.setSessionManager(sessionManager);

        log.info("=================SecurityManager类注入成功=================");
        return securityManager;
    }

    /**
     * 先走 filter ，然后 filter 如果检测到请求头存在 token，则用 token 去 login，走 Realm 去验证
     */
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(
            @Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

//        // 添加自己的过滤器
//        Map<String, Filter> filterMap = new HashMap<>();
//        //设置我们自定义的过滤器
//        filterMap.put("cors", new CORSFilter());
//        shiroFilterFactoryBean.setFilters(filterMap);

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 设置拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //开放登陆接口
        filterChainDefinitionMap.put("/user/login", "anon");
        //开放注册接口
        filterChainDefinitionMap.put("/user/register", "anon");

        filterChainDefinitionMap.put("/user/registerValid", "anon");

        filterChainDefinitionMap.put("/image/*.jpg", "anon");
        //其余接口一律拦截
        //主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setLoginUrl("/unauth");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        log.info("=================Shiro拦截器工厂类注入成功=================");
        return shiroFilterFactoryBean;
    }


    /**
     * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
     * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解，会导致该方法无法映射请求，导致返回404。
     * 加入这项配置能解决这个bug
     * 指定强制使用cglib为action创建代理对象
     */
    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();

        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        log.info("=================解决Shiro注解bug类注入成功=================");
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 配置shiro跟spring的关联
     *
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        log.info("=================Shiro关联spring类注入成功=================");
        return advisor;
    }

    /**
     * lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
     * (可选)
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        log.info("=================lifecycleBeanPostProcessor生命周期类注入成功=================");
        return new LifecycleBeanPostProcessor();
    }




}

